using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using AStarAlgo;

/*
 *  Author : Bidou (http://www.csharpfr.com/auteurdetail.aspx?ID=13319)
 *  Blog   : http://blogs.developpeur.org/bidou/
 *  Date   : January 2007
 */

namespace AStarTester
{
    /// ----------------------------------------------------------------------------------------
    /// <summary>
    /// Represents a map with a UserControl.
    /// </summary>
    /// ----------------------------------------------------------------------------------------
    public partial class Laby : UserControl
    {
        private const int _BORDERSIZE = 3;
        private bool _doPause = false;
        private int _boxSize = 30;
        private Map _map = null;
        private List<MapPoint> _shortestPath = new List<MapPoint>();
        private Thread _drawThread = null;

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Create a new Laby.
        /// </summary>
        /// ----------------------------------------------------------------------------------------
        public Laby()
        {
            this.InitializeComponent();
        }

        #region Draw

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Draw the shortest path.
        /// </summary>
        /// ----------------------------------------------------------------------------------------
        public void DrawShortestPath()
        {
            this._doPause = true;
            // Draw the path
            if (this._drawThread != null)
            {
                this._drawThread.Abort();
                this.Invalidate(false);
            }
            this._drawThread = new Thread(new ThreadStart(this.DrawLaby));
            this._drawThread.Priority = ThreadPriority.Highest;
            this._drawThread.Start();
        }

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Paint method.
        /// </summary>
        /// <param name="e"></param>
        /// ----------------------------------------------------------------------------------------
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (this._map != null)
            {
                int BoxXY = this._map.Length;
                // The rectangles
                Rectangle rect = new Rectangle(Point.Empty, new Size(this._boxSize, this._boxSize));
                for (int i = 0; i < BoxXY; i++)
                {
                    rect.Location = new Point(_BORDERSIZE, this._boxSize * i + _BORDERSIZE * (i + 1));
                    for (int j = 0; j < BoxXY; j++)
                    {
                        rect.Location = new Point(this._boxSize * j + _BORDERSIZE * (j + 1), rect.Location.Y);
                        e.Graphics.FillRectangle(new SolidBrush(Tools.GetColor(this._map[i, j])), rect);
                    }
                }
                // Start and end point
                this.DrawStartPoint(e.Graphics);
                this.DrawEndPoint(e.Graphics);
                // Draw the path
                this.DrawLaby();
            }
        }

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Draw the path.
        /// </summary>
        /// ----------------------------------------------------------------------------------------
        private void DrawLaby()
        {
            using (Brush pathBrush = new SolidBrush(Color.DarkBlue))
            {
                using (Graphics grx = this.CreateGraphics())
                {
                    foreach (MapPoint labPt in this._shortestPath)
                    {
                        if (labPt != this._map.StartPoint && labPt != this._map.EndPoint)
                        {
                            Rectangle curRect = this.GetRectangleFromPoint(labPt);
                            curRect.Inflate(-this._boxSize / 3, -this._boxSize / 3);
                            grx.FillRectangle(pathBrush, curRect);
                            if (this._doPause) Thread.Sleep(100);
                        }
                    }
                }
            }
            this._doPause = false;
        }

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Draw the start point.
        /// </summary>
        /// <param name="g"> The current graphics. </param>
        /// ----------------------------------------------------------------------------------------
        private void DrawStartPoint(Graphics g)
        {
            Pen cross = new Pen(Color.Red, 5);
            Rectangle rect = this.GetRectangleFromPoint(this._map.StartPoint);
            g.DrawLine(cross, rect.Left + 10, rect.Top + 10, rect.Right - 10, rect.Bottom - 10);
            g.DrawLine(cross, rect.Left + 10, rect.Bottom - 10, rect.Right - 10, rect.Top + 10);
        }

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Draw the end point.
        /// </summary>
        /// <param name="g"> The current graphics. </param>
        /// ----------------------------------------------------------------------------------------
        private void DrawEndPoint(Graphics g)
        {
            Pen circle = new Pen(Color.Green, 5);
            Rectangle rect = this.GetRectangleFromPoint(this._map.EndPoint);
            rect.Inflate(-8, -8);
            g.DrawEllipse(circle, rect);
        }

        #endregion

        #region Properties

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Get or set the box size.
        /// </summary>
        /// ----------------------------------------------------------------------------------------
        public int BoxSize
        {
            get { return this._boxSize; }
            set { this._boxSize = value; }
        }

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Get the shortest path.
        /// </summary>
        /// ----------------------------------------------------------------------------------------
        public List<MapPoint> ShortestPath
        {
            get { return this._shortestPath; }
        }

        #endregion

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Let the user loap a map.
        /// </summary>
        /// <param name="fullPathName"> The full map's path. </param>
        /// ----------------------------------------------------------------------------------------
        public void LoadMap(string fullPathName)
        {
            this._map = new Map(fullPathName);
            int gameXY = (this._map.Length + 1) * _BORDERSIZE + this._map.Length * this._boxSize;
            this.ClientSize = new Size(gameXY, gameXY);
            this.ShortestPath.Clear();
            this.Refresh();
        }

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Calculate the shortest path.
        /// </summary>
        /// ----------------------------------------------------------------------------------------
        public void CalculateShortestPath()
        {
            this._shortestPath.Clear();
            if (this._map != null && this._map.StartPoint != MapPoint.InvalidPoint && this._map.EndPoint != MapPoint.InvalidPoint)
            {
                AStar AStar = new AStar(this._map);
                List<MapPoint> sol = AStar.CalculateBestPath();
                if (sol != null) this._shortestPath.AddRange(sol);
            }
        }

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Get the current box (rectangle) from a point.
        /// </summary>
        /// <param name="labyPt"> The point. </param>
        /// <returns> the rectangle. </returns>
        /// ----------------------------------------------------------------------------------------
        private Rectangle GetRectangleFromPoint(MapPoint labyPt)
        {
            return new Rectangle((labyPt.X + 1) * _BORDERSIZE + labyPt.X * this._boxSize, (labyPt.Y + 1) * _BORDERSIZE + labyPt.Y * this._boxSize, this._boxSize, this._boxSize);
        }
    }
}